-- -------------------------------------------------------------------------
-- Advanced Debugger Module for ClickUI
-- -------------------------------------------------------------------------
-- Improvements:
-- - File logging to ClickUl/logs/ directory
-- - Integration with ClickUI notification system
-- - Module-specific logging support
-- - Log rotation and size limits
-- - Performance optimization marker tracking
-- -------------------------------------------------------------------------

local debugger = {}

-- [Lua 5.4] <const> for function references and static data
local unpack <const> = table.unpack or unpack
local os_clock <const> = os.clock
local table_insert <const> = table.insert
local table_concat <const> = table.concat
local table_remove <const> = table.remove
local string_format <const> = string.format
local tostring <const> = tostring
local type <const> = type
local pairs <const> = pairs
local ipairs <const> = ipairs
local pcall <const> = pcall
local math_floor <const> = math.floor
local io_queue = nil  -- Deferred IO queue (lazy loaded)

-- Configuration
local config = {
    enabled = true,              -- Master switch
    log_level = 1,               -- 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR
    show_timestamp = true,       -- Show timestamp in logs
    show_memory = false,         -- Show memory usage
    show_performance = false,    -- Show performance metrics
    log_to_file = true,          -- Log to file (now implemented)
    print_to_console = false,    -- Disabled to prevent Lexis from capturing output
    max_log_history = 100,       -- Max number of logs to keep
    module_filter = nil,         -- Filter logs by module (nil = all)
}

-- Log levels
local LOG_LEVELS = {
    DEBUG = { level = 1, name = "DEBUG", color = {100, 150, 255} },
    INFO  = { level = 2, name = "INFO",  color = {255, 255, 255} },
    WARN  = { level = 3, name = "WARN",  color = {255, 200, 0} },
    ERROR = { level = 4, name = "ERROR", color = {255, 50, 50} },
}

-- Storage
local log_history = {}
local log_history_head = 1  -- Circular buffer head index
local log_history_count = 0 -- Current number of logs
local performance_marks = {}
local performance_log_last = {}
local performance_log_interval = 1.0
local watched_vars = {}
local frame_times = {}
local last_frame_time = 0
local last_mem_log_time = 0
local last_mem_value = 0

-- File logging state
local log_file_handle = nil
local current_log_file_path = nil
local session_timestamp = nil  -- Initialized once per session for unique log files

-- Dependencies (ClickUI integration)
local show_notify = nil   -- ClickUI notification function

-- -------------------------------------------------------------------------
-- Usage Examples
-- -------------------------------------------------------------------------
--[[
    Example 1: Basic logging
        debugger.info("Application started")
        debugger.debug("Checking configuration")
        debugger.warn("Low memory detected")
        debugger.error("Failed to load resource")

    Example 2: Module-specific logging
        debugger.info("[Renderer]", "Vec2 cache initialized")
        debugger.debug("[Weapon]", "Loaded 800 weapons")
        debugger.warn("[Player]", "High player count", {count = 30})

    Example 3: Initialization with ClickUI
        local notification = require("ClickUl.Lib.notification")
        local debugger = require("ClickUl.Lib.debugger")

        debugger.init({
            log_level = 2,                -- INFO and above
            log_to_file = true,           -- Enable file logging
            show_performance = true,      -- Show performance metrics
        }, notification.show)

    Example 4: Performance tracking
        debugger.mark_start("render_ui")
        render_ui()
        debugger.mark_end("render_ui")  -- Logs: "⏱️ render_ui took X.XX ms"

    Example 5: Performance optimization tracking
        -- In optimized code:
        debugger.mark_perf_opt("Vec2 Flat Hash", "renderer")
        debugger.mark_perf_opt("Slider Cache", "widgets")

        -- Later:
        debugger.log_perf_opt_summary()
        -- Output:
        -- === Performance Optimization Summary ===
        -- Category: renderer
        --   - Vec2 Flat Hash: 1234 times
        -- Category: widgets
        --   - Slider Cache: 567 times
        -- === End Summary ===

    Example 6: Module filter
        debugger.init({module_filter = "[Renderer]"})
        debugger.info("[Renderer]", "This will be shown")
        debugger.info("[Weapon]", "This will be hidden")

    Example 7: Shutdown (important for file logging)
        debugger.shutdown()  -- Closes log file properly
]]

-- -------------------------------------------------------------------------
-- File System Helpers
-- -------------------------------------------------------------------------
local function trim(s)
    if type(s) ~= "string" then return "" end
    return (s:gsub("^%s+", ""):gsub("%s+$", ""))
end

local function normalize_module(module)
    if type(module) ~= "string" then return nil end
    local m = trim(module)
    if m == "" then return nil end
    if m:sub(1, 1) == "[" and m:sub(-1) == "]" then
        m = trim(m:sub(2, -2))
    end
    if m == "" then return nil end
    return m
end


local function get_log_dir()
    -- Try to use paths.script if available, otherwise use relative path
    local base = ".\\"

    -- Check if paths global is available (from main loader)
    if _G.paths and _G.paths.script then
        base = _G.paths.script
    end

    local last_char = base:sub(-1)
    if last_char ~= "\\" and last_char ~= "/" then
        base = base .. "\\"
    end

    return base .. "ClickUl\\logs\\"
end

local function ensure_log_dir()
    local log_dir = get_log_dir()

    -- Try to create directory (dirs API if available)
    if _G.dirs and _G.dirs.create then
        local ok = pcall(_G.dirs.create, log_dir)
        if not ok and config.print_to_console then
            print("[Debugger] Warning: Could not create log directory: " .. log_dir)
        end
    end

    return log_dir
end

local function get_log_file_path()
    local log_dir = ensure_log_dir()
    -- Initialize session timestamp once (persists for entire session)
    if not session_timestamp then
        session_timestamp = os.date and os.date("%Y-%m-%d") or "unknown"
    end
    return log_dir .. "debug_" .. session_timestamp .. ".log"
end

local function safe_close(handle)
    if not handle then return end
    if type(handle.close) == "function" then pcall(handle.close, handle) end
    if type(handle.Close) == "function" then pcall(handle.Close, handle) end
end

local function create_log_writer(file_path)
    local writer = { path = file_path }

    function writer:write(data)
        if not _G.file or not _G.file.open then
            return false
        end

        local f = _G.file.open(self.path, { create_if_not_exists = true, append = true })
        if f and (f.valid == nil or f.valid) then
            f.text = data
            safe_close(f)
            return true
        end
        safe_close(f)
        return false
    end

    function writer:setvbuf(_) end
    function writer:close() end

    return writer
end

local function open_log_file()
    if not config.log_to_file then return end

    -- Close existing handle (prevents leaks when init() is called multiple times)
    if log_file_handle then
        pcall(function() log_file_handle:close() end)
        log_file_handle = nil
    end

    local file_path = get_log_file_path()
    current_log_file_path = file_path

    if not _G.file or not _G.file.open then
        if config.print_to_console then
            print("[Debugger] Warning: File API missing, cannot open log file: " .. file_path)
        end
        return
    end

    local ok, ready = pcall(function()
        local f = _G.file.open(file_path, { create_if_not_exists = true, append = true })
        if f and (f.valid == nil or f.valid) then
            safe_close(f)
            return true
        end
        safe_close(f)
        return false
    end)

    if ok and ready then
        log_file_handle = create_log_writer(file_path)
        log_file_handle:setvbuf("no")
        if io_queue then
            io_queue.set_log_handle(log_file_handle)
        end
    elseif config.print_to_console then
        print("[Debugger] Warning: Could not open log file: " .. file_path)
    end
end

local function close_log_file()
    if log_file_handle then
        log_file_handle:close()
        log_file_handle = nil
    end
    if io_queue then
        io_queue.set_log_handle(nil)
    end
end

local function write_to_file(message)
    if not config.log_to_file then return end

    -- Lazy load io_queue
    if not io_queue then
        local ok, result = pcall(require, "ClickUl.Lib.io_queue")
        if ok then
            io_queue = result
            io_queue.set_log_handle(log_file_handle)
        end
    end

    -- Use deferred queue if available
    if io_queue then
        io_queue.queue_log(message)
        -- Update file handle if changed
        if log_file_handle and io_queue.get_log_handle() ~= log_file_handle then
            io_queue.set_log_handle(log_file_handle)
        end
        return
    end

    -- Fallback: immediate write (original behavior)
    if not log_file_handle then return end

    local ok, err = pcall(function()
        log_file_handle:write(message .. "\n")
    end)

    if not ok then
        if config.print_to_console then
            print("[Debugger] File write error: " .. tostring(err))
        end
        close_log_file()
    end
end

-- -------------------------------------------------------------------------
-- Initialization
-- -------------------------------------------------------------------------

-- Safe wrapper for collectgarbage
local function safe_gc(mode)
    if not collectgarbage then return 0 end
    local ok, result = pcall(collectgarbage, mode)
    return ok and result or 0
end

function debugger.init(options, clickui_notify_func)
    if options then
        for k, v in pairs(options) do
            if config[k] ~= nil then
                config[k] = v
            end
        end
    end

    -- Setup ClickUI notification function
    if clickui_notify_func and type(clickui_notify_func) == "function" then
        show_notify = clickui_notify_func
    end

    -- Open log file
    if config.log_to_file then
        open_log_file()
    else
        close_log_file()
    end

    debugger.info("[System]", "Debugger initialized")
end

function debugger.shutdown()
    debugger.info("[System]", "Debugger shutting down")
    close_log_file()
end

-- -------------------------------------------------------------------------
-- Configuration
-- -------------------------------------------------------------------------
function debugger.set_level(level)
    if type(level) == "string" then
        local level_data = LOG_LEVELS[level:upper()]
        if level_data then
            config.log_level = level_data.level
        end
    elseif type(level) == "number" then
        config.log_level = level
    end
end

function debugger.enable()
    config.enabled = true
end

function debugger.disable()
    config.enabled = false
end

function debugger.toggle()
    config.enabled = not config.enabled
    return config.enabled
end

function debugger.set_log_to_file(enabled)
    local on = (enabled == true)
    config.log_to_file = on

    if on then
        open_log_file()
    else
        close_log_file()
    end
end

function debugger.reopen_log_file()
    if not config.log_to_file then return false end
    open_log_file()
    return log_file_handle ~= nil
end

function debugger.get_log_file_path()
    return get_log_file_path()
end

function debugger.set_module_filter(filter)
    if filter == nil then
        config.module_filter = nil
        return
    end
    if type(filter) ~= "string" then
        filter = tostring(filter)
    end
    config.module_filter = normalize_module(filter)
end

-- -------------------------------------------------------------------------
-- Core Logging
-- -------------------------------------------------------------------------
local function format_message(level_data, module, message, data)
    local parts = {}

    -- Timestamp
    if config.show_timestamp then
        local time = os.date and os.date("%H:%M:%S") or ""
        table_insert(parts, "[" .. time .. "]")
    end

    -- Level
    table_insert(parts, "[" .. level_data.name .. "]")

    -- Module (if provided)
    if module and module ~= "" then
        table_insert(parts, "[" .. module .. "]")
    end

    -- Message
    table_insert(parts, tostring(message))

    -- Additional data
    if data ~= nil then
        table_insert(parts, "-> " .. debugger.serialize(data))
    end

    -- Memory
    if config.show_memory then
        local now = os_clock and os_clock() or 0
        if now - last_mem_log_time >= 1.0 then
            last_mem_log_time = now
            last_mem_value = math_floor(safe_gc("count"))
            table_insert(parts, string_format("[Mem: %d KB]", last_mem_value))
        end
    end

    return table_concat(parts, " ")
end

local function add_to_history(level_data, module, formatted_msg)
    local entry = {
        level = level_data.name,
        module = module or "",
        message = formatted_msg,
        time = os.clock and os.clock() or 0,
        color = level_data.color
    }

    -- Circular buffer: Overwrite oldest entry when full
    if log_history_count < config.max_log_history then
        log_history_count = log_history_count + 1
        table_insert(log_history, entry)
    else
        -- Overwrite at head position
        log_history[log_history_head] = entry
        log_history_head = (log_history_head % config.max_log_history) + 1
    end
end

local function log_internal(level_data, module, message, data)
    if not config.enabled then return end
    if level_data.level < config.log_level then return end

    local norm_module = normalize_module(module)

    -- Module filter
    if config.module_filter ~= nil then
        local norm_filter = normalize_module(config.module_filter)
        if norm_filter and norm_module ~= norm_filter then return end
    end

    local formatted = format_message(level_data, norm_module, message, data)

    -- Add to history
    add_to_history(level_data, norm_module, formatted)

    -- Output to console (disabled by default to prevent Lexis log capture)
    if config.print_to_console then
        print(formatted)
    end

    -- Write to file
    write_to_file(formatted)

    -- Show notification for errors/warnings (use ClickUI notification)
    if level_data.level >= LOG_LEVELS.WARN.level and show_notify then
        local notify_type = level_data.level >= LOG_LEVELS.ERROR.level and "Error" or "Warning"
        show_notify(tostring(message), notify_type)
    end
end

-- -------------------------------------------------------------------------
-- Public Logging Functions
-- -------------------------------------------------------------------------
-- Enhanced versions with module support
function debugger.debug(module_or_message, message_or_data, data)
    if message_or_data ~= nil then
        -- Called with module: debug(module, message, data)
        log_internal(LOG_LEVELS.DEBUG, module_or_message, message_or_data, data)
    else
        -- Called without module: debug(message)
        log_internal(LOG_LEVELS.DEBUG, nil, module_or_message, nil)
    end
end

function debugger.info(module_or_message, message_or_data, data)
    if message_or_data ~= nil then
        log_internal(LOG_LEVELS.INFO, module_or_message, message_or_data, data)
    else
        log_internal(LOG_LEVELS.INFO, nil, module_or_message, nil)
    end
end

function debugger.warn(module_or_message, message_or_data, data)
    if message_or_data ~= nil then
        log_internal(LOG_LEVELS.WARN, module_or_message, message_or_data, data)
    else
        log_internal(LOG_LEVELS.WARN, nil, module_or_message, nil)
    end
end

function debugger.error(module_or_message, message_or_data, data)
    if message_or_data ~= nil then
        log_internal(LOG_LEVELS.ERROR, module_or_message, message_or_data, data)
    else
        log_internal(LOG_LEVELS.ERROR, nil, module_or_message, nil)
    end
end

-- -------------------------------------------------------------------------
-- Performance Optimization Tracking
-- -------------------------------------------------------------------------
-- Track performance optimizations (works with [PERF OPT] markers)
local perf_opt_stats = {}

function debugger.mark_perf_opt(opt_name, category)
    if not config.enabled then return end

    local cat = tostring(category or "general")
    local name = tostring(opt_name or "unknown")
    local now = os.clock and os.clock() or 0

    local by_cat = perf_opt_stats[cat]
    if not by_cat then
        by_cat = {}
        perf_opt_stats[cat] = by_cat
    end

    local stat = by_cat[name]
    if not stat then
        stat = { time = now, count = 0 }
        by_cat[name] = stat
    end
    stat.time = now
    stat.count = stat.count + 1

    if config.show_performance then
        debugger.debug("[PERF OPT]", string_format("%s/%s applied", cat, name))
    end
end

function debugger.get_perf_opt_stats()
    return perf_opt_stats
end

function debugger.log_perf_opt_summary()
    if not config.enabled then return end

    debugger.info("[PERF OPT]", "=== Performance Optimization Summary ===")

    for category, opts in pairs(perf_opt_stats) do
        debugger.info("[PERF OPT]", string_format("Category: %s", category))

        for opt_name, stat in pairs(opts) do
            debugger.info("[PERF OPT]", string_format("  - %s: %d times", opt_name, stat.count))
        end
    end

    debugger.info("[PERF OPT]", "=== End Summary ===")
end

-- -------------------------------------------------------------------------
-- Performance Monitoring
-- -------------------------------------------------------------------------
function debugger.mark_start(name)
    if not config.enabled then return end
    if not name or name == "" then return end
    performance_marks[tostring(name)] = os.clock and os.clock() or 0
end

function debugger.mark_end(name)
    if not config.enabled then return end

    if not name or name == "" then return end
    local key = tostring(name)

    local start_time = performance_marks[key]
    if not start_time then
         debugger.warn("Performance mark not found: " .. key)
        return
    end

    local elapsed = (os.clock and os.clock() or 0) - start_time
    local ms = elapsed * 1000

    if config.show_performance then
        local now = os_clock and os_clock() or 0
        local last_log = performance_log_last[key] or 0
        if performance_log_interval <= 0 or (now - last_log) >= performance_log_interval then
            debugger.debug(string_format("⏱️ %s took %.2f ms", name, ms))
            performance_log_last[key] = now
        end
    end

    performance_marks[key] = nil
    return ms
end

function debugger.profile(name, func)
    if not config.enabled then
        return func()
    end

    local key = tostring(name or "profile")
    debugger.mark_start(key)

    local results = { pcall(func) }
    debugger.mark_end(key)

    if not results[1] then
        debugger.error(string_format("Profiled function '%s' failed: %s", key, tostring(results[2])))
        error(results[2])
    end

    if unpack then
        return unpack(results, 2)
    end
    return results[2]
end

-- -------------------------------------------------------------------------
-- Variable Watching
-- -------------------------------------------------------------------------
function debugger.watch(var_name, value)
    if not config.enabled then return end

    local old_value = watched_vars[var_name]
    watched_vars[var_name] = value

    if old_value ~= nil and old_value ~= value then
        debugger.debug(string_format("👁️ %s changed: %s -> %s",
            var_name,
            debugger.serialize(old_value),
            debugger.serialize(value)
        ))
    end
end

function debugger.get_watched()
    return watched_vars
end

function debugger.clear_watched()
    watched_vars = {}
end

-- -------------------------------------------------------------------------
-- Memory Monitoring
-- -------------------------------------------------------------------------
function debugger.get_memory()
    local count = safe_gc("count")
    return {
        kb = count,
        mb = count / 1024
    }
end

function debugger.log_memory(label)
    if not config.enabled then return end

    local mem = debugger.get_memory()
    debugger.info(string_format("💾 Memory%s: %.2f MB (%.0f KB)",
        label and (" [" .. label .. "]") or "",
        mem.mb,
        mem.kb
    ))
end

function debugger.force_gc()
    safe_gc("collect")
    debugger.log_memory("After GC")
end

-- -------------------------------------------------------------------------
-- Stack Trace
-- -------------------------------------------------------------------------
function debugger.trace()
    if not config.enabled then return end

    if not debug or not debug.traceback then
        debugger.warn("Stack trace unavailable: debug library not found")
        return
    end

    local ok, trace = pcall(debug.traceback, "", 2)
    if ok and trace then
        debugger.debug("Stack trace:")
        debugger.debug(trace)
    else
        debugger.warn("Failed to generate stack trace", tostring(trace))
    end
end

-- -------------------------------------------------------------------------
-- Object Serialization
-- -------------------------------------------------------------------------
function debugger.serialize(obj, max_depth)
    max_depth = max_depth or 3

    local function serialize_internal(o, depth)
        if depth > max_depth then
            return "..."
        end

        local t = type(o)

        if t == "nil" then
            return "nil"
        elseif t == "boolean" or t == "number" then
            return tostring(o)
        elseif t == "string" then
            return string_format("%q", o)
        elseif t == "table" then
            local parts = {}
            local count = 0

            for k, v in pairs(o) do
                count = count + 1
                if count > 10 then
                    table_insert(parts, "...")
                    break
                end

                local key = type(k) == "string" and k or ("[" .. tostring(k) .. "]")
                local value = serialize_internal(v, depth + 1)
                table_insert(parts, key .. "=" .. value)
            end

            return "{" .. table_concat(parts, ", ") .. "}"
        else
            return tostring(o)
        end
    end

    return serialize_internal(obj, 0)
end

-- -------------------------------------------------------------------------
-- Frame Time Monitoring
-- -------------------------------------------------------------------------
function debugger.update_frame_time()
    local now = os.clock and os.clock() or 0

    if last_frame_time > 0 then
        local frame_time = now - last_frame_time
        table_insert(frame_times, frame_time)

        -- Keep last 60 frames
        if #frame_times > 60 then
            table_remove(frame_times, 1)
        end
    end

    last_frame_time = now
end

function debugger.get_avg_frame_time()
    if #frame_times == 0 then return 0 end

    local sum = 0
    for _, t in ipairs(frame_times) do
        sum = sum + t
    end

    return sum / #frame_times
end

function debugger.get_fps()
    local avg = debugger.get_avg_frame_time()
    if avg > 0 then
        return 1 / avg
    end
    return 0
end

-- -------------------------------------------------------------------------
-- History & Export
-- -------------------------------------------------------------------------
function debugger.get_history()
    return log_history
end

local function build_history_ordered()
    local ordered = {}
    local size = #log_history
    if size == 0 then return ordered end

    -- Not full yet -> already chronological
    if log_history_count < config.max_log_history then
        for i = 1, size do ordered[i] = log_history[i] end
        return ordered
    end

    -- Full ring buffer: head points to the next slot to overwrite (oldest entry)
    local idx = log_history_head
    for i = 1, size do
        ordered[i] = log_history[idx]
        idx = (idx % size) + 1
    end
    return ordered
end

function debugger.get_history_ordered()
    return build_history_ordered()
end

function debugger.clear_history()
    local cleared_count = log_history_count
    log_history = {}
    log_history_head = 1
    log_history_count = 0
    return cleared_count
end

function debugger.get_history_count()
    return log_history_count
end

function debugger.dump_history()
    debugger.info("=== Debug History ===")
    local size = #log_history
    if size > 0 then
        if log_history_count < config.max_log_history then
            for i = 1, size do
                local log = log_history[i]
                if config.print_to_console then
                    print(log.message)
                end
                write_to_file(log.message)
            end
        else
            local idx = log_history_head
            for _ = 1, size do
                local log = log_history[idx]
                if config.print_to_console then
                    print(log.message)
                end
                write_to_file(log.message)
                idx = (idx % size) + 1
            end
        end
    end
    debugger.info("=== End History ===")
end

-- -------------------------------------------------------------------------
-- Assertion
-- -------------------------------------------------------------------------
function debugger.assert(condition, message)
    if not condition then
        debugger.error("Assertion failed: " .. (message or "no message"))
        debugger.trace()
    end
end

-- -------------------------------------------------------------------------
-- Get Config
-- -------------------------------------------------------------------------
function debugger.get_config()
    return config
end

-- -------------------------------------------------------------------------
-- Module Helpers
-- -------------------------------------------------------------------------
-- Create module-specific logger (convenience function)
function debugger.create_logger(module_name)
    return {
        debug = function(msg, data) debugger.debug(module_name, msg, data) end,
        info = function(msg, data) debugger.info(module_name, msg, data) end,
        warn = function(msg, data) debugger.warn(module_name, msg, data) end,
        error = function(msg, data) debugger.error(module_name, msg, data) end,
        mark_start = function(name) debugger.mark_start(module_name .. ":" .. name) end,
        mark_end = function(name) return debugger.mark_end(module_name .. ":" .. name) end,
    }
end

--[[
    Usage example with module logger:

    local log = debugger.create_logger("[Renderer]")
    log.info("Initializing renderer")
    log.debug("Loading shaders", {count = 10})
    log.warn("Low VRAM detected")

    log.mark_start("render_frame")
    -- ... render code ...
    log.mark_end("render_frame")
]]

-- -------------------------------------------------------------------------
-- ClickUI Integration Improvements Summary
-- -------------------------------------------------------------------------
--[[
    IMPROVEMENTS MADE TO debugger.lua FOR ClickUI PROJECT:

    1. FILE LOGGING TO ClickUl/logs/
        - Logs written to: ClickUl/logs/debug_YYYY-MM-DD.log
        - Disabled buffering for immediate writes

    2. MODULE-SPECIFIC LOGGING
       - Support for module tags: debugger.info("[Renderer]", "message")
       - Module filtering: only show logs from specific modules
       - Create dedicated loggers per module

    3. ClickUI NOTIFICATION INTEGRATION
       - Warnings and errors trigger ClickUI notifications
       - Replaced generic notify_push with ClickUI's show_notify()
       - Clean integration with existing notification system

    4. PERFORMANCE OPTIMIZATION TRACKING
       - New function: debugger.mark_perf_opt(name, category)
       - Works with [PERF OPT] markers in code
       - Can generate optimization summary reports
       - Useful for tracking which optimizations are being used

    5. IMPROVED CONFIGURATION
        - Added module_filter: show only specific modules
        - log_to_file now enabled by default

    6. BETTER ERROR HANDLING
       - Safe pcall wrappers for all file operations
       - Automatic log file recovery on errors
       - No crashes if directory creation fails

    7. USAGE EXAMPLES
       - Comprehensive inline documentation
       - Example code for all major features
       - Integration examples with ClickUI

    INITIALIZATION PATTERN:
        -- In ClickUl_Loader.lua or main.lua
        local notification = require("ClickUl.Lib.notification")
        local debugger = require("ClickUl.Lib.debugger")

        debugger.init({
            log_level = 2,              -- INFO and above
            log_to_file = true,
            show_performance = false,
        }, notification.show)

        -- At shutdown:
        debugger.shutdown()

    MIGRATION FROM OLD CODE:
        Old: debugger.info("message")
        New: debugger.info("message")  -- Still works!

        Old: N/A
        New: debugger.info("[Module]", "message")  -- Module support

        Old: N/A
        New: local log = debugger.create_logger("[Module]")  -- Dedicated logger
]]

return debugger
